/*
	How to compile: gcc -lrt -o shmCreatorMutex shmCreatorMutex.c 

	Description: A shared memory segment is "allocated" using the getSharedMemoryArray function.
	A POSIX semaphore (mutex) is used to control the access to the critical region.

	POSIX semamphore man page abstract: The sem_open(3) function creates a new named semaphore or opens an existing named semaphore. After the semaphore has been opened, it can be operated on using sem_post(3) and sem_wait(3). When a process has finished using the semaphore, it can use sem_close(3) to close the semaphore. When all processes have finished using the semaphore, it can be removed from the system using sem_unlink(3).

	Author: Hugo Mendez @ ITESM CCV Sep 2011
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <semaphore.h>

#define SHMSZ 27
char SEM_NAME[]= "/mutexSem";

char *getSharedMemorySegment(int *segment_id);
void freeSharedMemorySegment(char *shm, int segment_id);

int main (int argc, const char * argv[]) {
	
	//Get a pointer to shared memory byte arrary
	int segment_id;
	char *s;
	
	sem_t *sem_mutex;	//Variable to store the semaphore address
	sem_mutex = sem_open(SEM_NAME, O_CREAT, 0644, 1);	//semaphore  global name: mutex

	if(sem_mutex == SEM_FAILED)
	{
		perror("unable to create semaphore");
		sem_unlink(SEM_NAME);
		return -1;
	}

	char *shm = getSharedMemorySegment(&segment_id);
	printf("segment id: %d\n", segment_id);
	s=shm;
	
	//if another process wants to use this semaphore it will have to use the global name
		//This is our critical region
	//Let's write something into this "shared array"

	printf("Writing...\n");	
	//strcpy(s, "Hola mundo");
	char ch;
	int semVal;

	for(ch='A';ch<='Z';ch++)
    {
	
	sem_getvalue(sem_mutex, &semVal);	
	printf("sem value: %d\n", semVal);
	if(sem_trywait(sem_mutex) == -1){ 
		printf("posting...\n");		
		sem_post(sem_mutex);
		sem_wait(sem_mutex);
	}
	sem_getvalue(sem_mutex, &semVal);	
	printf("sem value after wait: %d\n", semVal);

      *s++ = ch;
	printf("*s= %c", *(s-1));
      sem_post(sem_mutex);
		sem_getvalue(sem_mutex, &semVal);	
	printf("sem value after post: %d\n", semVal);

    }
	/*
	* Finally, we wait until the other process
	* changes the first character of our memory
	* to '*', indicating that it has read what
	* we placed in there.
	*/

	//This can't be a critical region, why?
	//As we are waiting for someone to write into the shared memory but nobody
	//can enter the critical region to write something, we'll be waiting forever!
	while (*shm != '*')
	sleep(1);

	//Now nobody else will be using the semaphore, so let's close it
	sem_close(sem_mutex);
	//Finally let's remove from the system, otherwise it will be a zombie in sem_zombieland
	sem_unlink(SEM_NAME);

	//If we are done, detach and deallocate the memory segment
	freeSharedMemorySegment(shm, segment_id);
	return 0;
}

char *getSharedMemorySegment(int *segment_id){
	key_t key; 
	key=ftok("shmCreatorMutex.c", '1');	//Will generate a key based on the name of this file and the number '1'
	int pageSize= getpagesize();  //how big is the size of a page memory in my system?
	//Let's allocate a memory segment to share with the world (one page of memory)
	//We'll get a unique memory segment identifier
	//*segment_id = shmget(key, pageSize, IPC_CREAT| IPC_EXCL | 0666); //everybody can read and write
	int sid = shmget(key, SHMSZ, IPC_CREAT| IPC_EXCL | 0666); 
	
	*segment_id=sid;
	//Check for problems
	if(*segment_id < 0){
	printf("Problemas al solicitar el segmento de memoria");
	exit(1);
	}

	char *shm;      //We need a byte pointer to have access to the shared memory

	//Let Linux choose an available space within our virtual memory space (that's the NULL)
	shm = shmat(*segment_id, NULL, 0);
	//shm "points" to what we can see as an array of memory locations
	return shm;
}

void freeSharedMemorySegment(char *shm, int segment_id){
	//First we need to detach the segment
	shmdt (shm);
	//We need to liberate the memory segment, otherwise it remains allocated
	//The segment is removed when the last process that has attached it finally detaches it.
	shmctl (segment_id, IPC_RMID, NULL);
}

/*
 Reference links:
 http://linux.die.net/man/7/sem_overview
 http://www.cs.cf.ac.uk/Dave/C/node26.html
 
 */

